﻿#region Header
// Copyright 2007-2011, Jerónimo Milea
// 
// This file is part of NetJsWire.
// 
//     NetJsWire is free software: you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation, either version 3 of the License, or
//     (at your option) any later version.
// 
//     NetJsWire is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.
// 
//     You should have received a copy of the GNU General Public License
//     along with NetJsWire.  If not, see <http://www.gnu.org/licenses/>.
#endregion

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using NetJsWire.Lite.Converters;
using NetJsWire.Lite.Designers;
using NetJsWire.Lite.Editors;
using NetJsWire.Lite.Helpers;
using NetJsWire.Lite.Properties;
using NetJsWire.Lite.Widgets.UI.Layout;
using Newtonsoft.Json;

namespace NetJsWire.Lite.Widgets.UI
{
    [ClassInterface( ClassInterfaceType.AutoDispatch ),
     ToolboxItem( false ),
     DesignTimeVisible( false ),
     Designer( typeof( WindowDesigner ), typeof( IRootDesigner ) ),
     DesignerCategory( "Form" ),
     DisplayName( "Window" )]
    [JsonObject( MemberSerialization = MemberSerialization.OptIn )]
    [Serializable]
    public class Window : Panel
    {
        private bool _maximizable;
        private bool _minimizable;

        public Window()
        {
            Layout = LayoutType.Absolute;
            Size = new Size( 400, 300 );

            if ( Settings.Default != null )
            {
                OriginalBackgroundColor = Settings.Default.WindowBackColor;
            }
            else
            {
                OriginalBackgroundColor = Color.FromArgb( 223, 232, 246 );
            }

            base.Xtype = null;
            base.Closable = true;
            Resizable = true;
            InitHidden = true;
            base.Hidden = true;
            base.BackColor = OriginalBackgroundColor;
            BackgroundColor = OriginalBackgroundColor;

            base.Shadow = true;
        }

        //[Browsable( true ), EditorBrowsable( EditorBrowsableState.Advanced )]
        //public override Color BackColor
        //{
        //    get { return base.BackColor; }
        //    set { base.BackColor = value; }
        //}

        [Browsable( false ), EditorBrowsable( EditorBrowsableState.Never )]
        public override bool Frame
        {
            get { return false; }
        }

        [JsonIgnore]
        [Editor( typeof( ScriptEditor ), typeof( UITypeEditor ) )]
        [DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden )]
        public string InternalPreCode { get; set; }

        [JsonIgnore]
        [Editor( typeof( ScriptEditor ), typeof( UITypeEditor ) )]
        [Description( "Code that should be executed before window declaration." )]
        public string PreCode { get; set; }

        [JsonIgnore]
        [Editor( typeof( ScriptEditor ), typeof( UITypeEditor ) )]
        [Description( "Code that should be executed after window declaration." )]
        public string PostCode { get; set; }

        [Browsable( false ), EditorBrowsable( EditorBrowsableState.Never )]
        public bool TopLevel { get; set; }

        [DefaultValue( LayoutType.Absolute )]
        [JsonProperty( "layout", DefaultValueHandling = DefaultValueHandling.Include )]
        [JsonConverter( typeof( LowerCaseEnumConverter ) )]
        [Description( "The layout type to be used by this window" )]
        public override sealed LayoutType Layout
        {
            get { return base.Layout; }
            set { base.Layout = value; }
        }

        [Browsable( false )]
        public override bool Border
        {
            get { return true; }
        }

        [DefaultValue( true )]
        [JsonProperty( "closable" )]
        [RefreshProperties( RefreshProperties.Repaint )]
        [Description( "True to display the 'close' tool button and allow the user to close the window, false to hide the button and disallow closing the window (defaults to true)." )]
        public override bool Closable
        {
            get { return base.Closable; }
            set { base.Closable = value; }
        }

        [DefaultValue( false )]
        [JsonProperty( "maximizable" )]
        [RefreshProperties( RefreshProperties.Repaint )]
        [Description( "True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button and disallow maximizing the window (defaults to false)." )]
        public bool Maximizable
        {
            get { return _maximizable; }
            set
            {
                _maximizable = value;
                InvalidateWindow();
            }
        }

        [DefaultValue( false )]
        [JsonProperty( "minimizable" )]
        [RefreshProperties( RefreshProperties.Repaint )]
        [Description( "True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button and disallow minimizing the window (defaults to false)." )]
        public bool Minimizable
        {
            get { return _minimizable; }
            set
            {
                _minimizable = value;
                InvalidateWindow();
            }
        }

        [Browsable( true )]
        [JsonProperty( "modal" )]
        [DefaultValue( false )]
        [Description( "True to make the window modal and mask everything behind it when displayed, false to display it without restricting access to other UI elements." )]
        public bool Modal { get; set; }

        [Browsable( true )]
        [JsonProperty( "constrain" )]
        [DefaultValue( false )]
        public bool Constrain { get; set; }

        [Browsable( true )]
        [JsonProperty( "hidden" )]
        [DefaultValue( true )]
        [Description( "Render this component hidden (default is true). If true, the hide method will be called internally." )]
        public override bool Hidden
        {
            get { return base.Hidden; }
            set { base.Hidden = value; }
        }

        [Browsable( true )]
        [JsonProperty( "initHidden" )]
        [DefaultValue( true )]
        [Description( "True to hide the window until show() is explicitly called (defaults to true)." )]
        public bool InitHidden { get; set; }

        [Browsable( true )]
        [JsonProperty( "resizable" )]
        [DefaultValue( true )]
        public bool Resizable { get; set; }

        [DefaultValue( true )]
        [RefreshProperties( RefreshProperties.All )]
        [Description( "true to display a shadow behind the Panel.\nNote that this options only applies when Floating = true" )]
        public override bool Shadow
        {
            get
            {
                return base.Shadow;
            }
            set
            {
                base.Shadow = value;
            }
        }

        [Browsable( true )]
        [JsonProperty( "constrainHeader" )]
        [DefaultValue( false )]
        public bool ConstrainHeader { get; set; }

        #region Implementation of ISerializer

        public override string Serialize()
        {
            if ( _serializingAsClass )
            {
                return base.Serialize();
            }

            var ret = string.Format( "new {0}({1});", "Ext.Window", base.Serialize() );

            if ( !string.IsNullOrWhiteSpace( PreCode ) )
            {
                var preC = PreCode;
                preC = preC.Replace( Environment.NewLine, "" );
                if ( !preC.EndsWith( ";" ) )
                    preC += ";";
                ret = string.Format( "{0}{1}", preC, ret );
            }
            if ( !string.IsNullOrWhiteSpace( InternalPreCode ) )
            {
                var preC = InternalPreCode;
                preC = preC.Replace( Environment.NewLine, "" );
                if ( !preC.EndsWith( ";" ) )
                    preC += ";";
                ret = string.Format( "{0}{1}", preC, ret );
                InternalPreCode = null;
            }

            if ( !string.IsNullOrWhiteSpace( PostCode ) )
                ret += PostCode.Replace( Environment.NewLine, "" );

            if ( !string.IsNullOrWhiteSpace( ret ) && !ret.EndsWith( ";" ) )
                ret += ";";

            return ret;
        }

        #endregion

        protected override void OnPaintNcArea( PaintEventArgs e )
        {
            var borderColor = Color.FromArgb( 142, 164, 193 );
            var innerBorderColor = Color.FromArgb( 227, 233, 237 );

            using ( var br = new SolidBrush( PanelNCBackgroundColor ) )
            {
                // Left side
                e.Graphics.FillRectangle( br, e.ClipRectangle.X + 2, e.ClipRectangle.Y + TitleBarHeight, 4,
                                          e.ClipRectangle.Height - TitleBarHeight - 5 - 2 );

                // Right side
                e.Graphics.FillRectangle( br, e.ClipRectangle.Width - 6, e.ClipRectangle.Y + TitleBarHeight, 4,
                                          e.ClipRectangle.Height - TitleBarHeight - 5 - 2 );

                // Bottom side
                e.Graphics.FillRectangle( br, e.ClipRectangle.X + 2, e.ClipRectangle.Height - 5 - 2,
                                          e.ClipRectangle.Width - 4, 5 );
            }

            // Outer border
            using ( var pen = new Pen( Color.White, 3 ) )
            {
                e.Graphics.DrawRectangle( pen, 0, 0, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1 );
            }
            // Outer border
            using ( var pen = new Pen( borderColor ) )
            {
                var roundRect = RoundedRectangle.Create( 0, 0, e.ClipRectangle.Width - 1, e.ClipRectangle.Height - 1,
                                                         2.5f );
                e.Graphics.DrawPath( pen, roundRect );
            }
            // Inner border
            using ( var pen = new Pen( innerBorderColor ) )
            {
                var roundRect = RoundedRectangle.Create( 1, 1, e.ClipRectangle.Width - 3, e.ClipRectangle.Height - 3,
                                                         2.5f );
                e.Graphics.DrawPath( pen, roundRect );
            }
            var rect = e.ClipRectangle;

            // Shift the outer border of the Window
            rect.Inflate( -2, -2 );
            base.OnPaintNcArea( new PaintEventArgs( e.Graphics, rect ) );
            return;
        }

        protected override void OnHandleCreated( EventArgs e )
        {
            Native.SetWindowTheme( Handle, "", "" );
            base.OnHandleCreated( e );
        }

        protected override void OnNonClientAreaCalcSize( ref Rectangle bounds, bool update )
        {
            var clientPadding = new Padding( 6 + BorderWidth, TitleBarHeight + BorderWidth, 6 + BorderWidth,
                                             ( DrawButtonsBar ? ButtonsBarHeight + 5 : 5 ) + ( BorderWidth * 2 ) );
            bounds = new Rectangle( bounds.Left + clientPadding.Left, bounds.Top + clientPadding.Top,
                                    bounds.Width - clientPadding.Horizontal, bounds.Height - clientPadding.Vertical );
        }

        protected override Rectangle GetScaledBounds( Rectangle bounds, SizeF factor, BoundsSpecified specified )
        {
            if ( ( specified & BoundsSpecified.Width ) == BoundsSpecified.Width )
            {
                var border = Size.Width - ClientSize.Width;
                bounds.Width = ( int ) Math.Round( ( bounds.Width - border ) * factor.Width ) + border;
            }
            if ( ( specified & BoundsSpecified.Height ) == BoundsSpecified.Height )
            {
                var border = Size.Height - ClientSize.Height;
                bounds.Height = ( int ) Math.Round( ( bounds.Height - border ) * factor.Height ) + border;
            }

            return bounds;
        }
    }
}